﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Configuration;
using Microsoft.Win32;

using DVBViMon.Utils;
using iMon.DisplayApi;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;


namespace DVBViMon
{
    public delegate void LCDScrollFinishedEventHandler(object obj, EventArgs e);

    public partial class FormMain : Form
    {
        #region LCD scroll finished event

        public event LCDScrollFinishedEventHandler LCDScrollFinished;

        #endregion

        #region enums

        public enum ConnectionStates
        {
            NoneActive,
            ComInterface,
            DisplayInterface,
            AllActive
        }

        public enum StateMachineStates
        {
            NoneActive,
            ActivateComInterface,
            WaitForComInterfaceActive,
            ActivateiMonInterface,
            WaitForiMonActive,
            AllActive
        }

        public enum iMonMessageStates
        {
            none,
            success,
            connectedRestarted,
            failed
        }

        #endregion

        #region fields

        private const int scrollDelay = 400; // scroll delay in ms
//        private Logger<LogEntry> logger;
        private DvbvComMngr dvbvComManager;
        private ScrollTextTimerVFD scrollTextTimerVFD;
        private ScrollTextTimerLCD scrollTextTimerLCD;

        private bool dvbvComInterfaceActive = false;
        private bool iMonDisplayInterfaceActive = false;
        private bool controlLoopActive = false;

        private iMonNativeApi.iMonDisplayType displayType = iMonNativeApi.iMonDisplayType.None;
        private iMonNativeApi.iMonDisplayInitResult lastErrorShown = iMonNativeApi.iMonDisplayInitResult.Unknown;

        private iMonMessageStates messageState = iMonMessageStates.none;
        private bool durationRemainingFlag = false;
        #endregion

        #region Constants
        private const int WM_DSP_PLUGIN_NOTIFY = 0x8000 + 1001;
        #endregion

        #region properties

        public ConnectionStates ConnectionState { get; private set; }
        public StateMachineStates MainLoopSM { get; private set; }

        #endregion

        #region ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="FormMain"/> class.
        /// </summary>
        public FormMain()
        {
            // set default culture to "english"
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            ConnectionState = ConnectionStates.AllActive;
            MainLoopSM = StateMachineStates.NoneActive;
            scrollTextTimerVFD = new ScrollTextTimerVFD(scrollDelay);
            scrollTextTimerLCD = new ScrollTextTimerLCD(this, scrollDelay);

            InitializeComponent();

            this.dvbvComManager = new DvbvComMngr();

            Application.Idle += new EventHandler(Application_Idle);
        }
        #endregion

        #region application idle event

        /// <summary>
        /// Handles the Idle event of the Application control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void Application_Idle(object sender, EventArgs e)
        {
            // if icons for notify are setup here, DVBViewerServer COM interrupts are no longer working!!

            // local copies to avoid glitches
            ConnectionStates oldState = this.ConnectionState;
            bool dvbvActive = this.dvbvComInterfaceActive;
            bool iMonActive = this.iMonDisplayInterfaceActive;

            if (dvbvActive && iMonActive)
            {
                this.ConnectionState = ConnectionStates.AllActive;
                if (oldState != this.ConnectionState)
                {
                    this.notifyIcon.Icon = Properties.Resources.IconMagicEye;
                    this.pictureBoxDvbvConnected.BackColor = Color.GreenYellow;
                    this.pictureBoxiMonConnected.BackColor = Color.GreenYellow;
                }
            }
            else if (!dvbvActive && !iMonActive)
            {
                this.ConnectionState = ConnectionStates.NoneActive;
                if (oldState != this.ConnectionState)
                {
                    this.notifyIcon.Icon = Properties.Resources.IconMagicEyeRed;
                    this.pictureBoxDvbvConnected.BackColor = Color.OrangeRed;
                    this.pictureBoxiMonConnected.BackColor = Color.OrangeRed;
                }
            }
            else if (!dvbvActive && iMonActive)
            {
                this.ConnectionState = ConnectionStates.DisplayInterface;
                if (oldState != this.ConnectionState)
                {
                    this.notifyIcon.Icon = Properties.Resources.IconMagicEyeYellow;
                    this.pictureBoxDvbvConnected.BackColor = Color.OrangeRed;
                    this.pictureBoxiMonConnected.BackColor = Color.YellowGreen;
                }
            }
            else
            {
                this.ConnectionState = ConnectionStates.ComInterface;
                if (oldState != this.ConnectionState)
                {
                    this.notifyIcon.Icon = Properties.Resources.IconMagicEyeYellow;
                    this.pictureBoxDvbvConnected.BackColor = Color.YellowGreen;
                    this.pictureBoxiMonConnected.BackColor = Color.OrangeRed;
                }
            }
//            Debug.Print("Idle");
        }
        #endregion

        #region disable close button

        private const int CP_NOCLOSE_BUTTON = 0x200;
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams myCp = base.CreateParams;
                myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON;
                return myCp;
            }
        }
        #endregion

        #region form events

        /// <summary>
        /// Handles the Load event of the Form1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Form1_Load(object sender, EventArgs e)
        {
            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();

//            ShowSomeSettings();
//            GetLCDFontInfoFromRegistry();

            this.Text = this.Text + " v" + this.ProductVersion;
            if (Properties.Settings.Default.UpdateRequired)
            {
                Properties.Settings.Default.Upgrade();
                Properties.Settings.Default.UpdateRequired = false;
                Properties.Settings.Default.Save();
            }

            if (Properties.Settings.Default.startMinimized)
            {
                this.WindowState = FormWindowState.Minimized;
            }

            logger.MaxEntries = Properties.Settings.Default.maxLogEntries;
            logger.Subscribe(this.loggingControl1.Update);
            logger.Write(this.Text + " started.",MessageLevel.Information);

            // turn on timer for dvbv connection check
            this.timerCheckDvbvConnection.Start();

            // turn on timer for iMon connection
            this.timerCheckiMonConnection.Start();

            // turn on timer for displaying media duration and media remaining time
            this.timerDurRem.Start();

            // start main control loop (1s)                  
            timerMainControlLoop.Start();
        }

        private void GetLCDFontInfoFromRegistry()
        {
            RegistryKey key;
            if (Environment.Is64BitOperatingSystem)
                key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432node\\SOUNDGRAPH\\iMEDIAN HD", false);
            else
                key = Registry.LocalMachine.OpenSubKey("Software\\SOUNDGRAPH\\iMEDIAN HD", false);

            if (key != null)
            {
                object obj = key.GetValue("Ver");
                if (obj != null)
                    Debug.Print("SOUNDGRAPH\\Ver = " + obj.ToString());
                key.Close();
            }
        }

        private void ShowSomeSettings()
        {
            Properties.Settings Props = Properties.Settings.Default;


            // using System.Configuration; (System.Configuration.dll)
            // ______________
            // USER-Settings:
            Configuration config = ConfigurationManager.OpenExeConfiguration(
                ConfigurationUserLevel.PerUserRoamingAndLocal);
            MessageBox.Show("Pfad zur 'user.config': " + config.FilePath);

            //Props.MeinBenutzer1 = "was neues";
            //Props.Save();
            //Process.Start("notepad", config.FilePath);


            // ____________________
            // Application-Settings:
            config = ConfigurationManager.OpenExeConfiguration(
                ConfigurationUserLevel.None);
            MessageBox.Show("Pfad zu Anwendungs-Settings: " + config.FilePath);
            //Process.Start("notepad", config.FilePath);


            // ____________________
            // Machine.config-Pfad:
            config = ConfigurationManager.OpenMachineConfiguration();
            MessageBox.Show("Pfad zur 'machine.config': " + config.FilePath);
            Process.Start("notepad", config.FilePath);
        }

        private void FormMain_Shown(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.startMinimized)
            {
                this.Hide();
            }
        }

        private void FormMain_Resize(object sender, EventArgs e)
        {
            if (FormWindowState.Minimized == WindowState)
                Hide();
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.dvbvComManager.UnsubscribeEvents();
        }
        #endregion

        #region implementation of main control loop

        private void timerMainControlLoop_Tick(object sender, EventArgs e)
        {
            if (this.controlLoopActive)
                return;
            this.controlLoopActive = true;

            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();
//            logger.Write("Test ", MessageLevel.Information, new string[] {"Subtext1", "Subtext2"});

            StateMachineStates localState = this.MainLoopSM;
            switch (localState)
            {
                case StateMachineStates.NoneActive:
                    this.dvbvComManager.connectToDVBViewerComInterface();
                    this.MainLoopSM = StateMachineStates.WaitForComInterfaceActive;
                    break;

                case StateMachineStates.WaitForComInterfaceActive:
                    if (this.dvbvComInterfaceActive)
                        this.MainLoopSM = StateMachineStates.ActivateiMonInterface;
                    else
                        this.MainLoopSM = StateMachineStates.NoneActive;
                    break;

                case StateMachineStates.ActivateiMonInterface:
                    this.messageState = iMonMessageStates.none;
                    this.resetInitData();
                    try
                    {
                        InitDisplay();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message.ToString(), "Error missing .dll iMONDisplay.dll");
                        Application.Exit();
                    }
                    if (!this.dvbvComInterfaceActive)
                        this.MainLoopSM = StateMachineStates.NoneActive;
                    else
                        this.MainLoopSM = StateMachineStates.WaitForiMonActive;
                    break;

                case StateMachineStates.WaitForiMonActive:
                    if (!this.dvbvComInterfaceActive)
                        this.MainLoopSM = StateMachineStates.NoneActive;
                    else if (this.messageState == iMonMessageStates.failed || this.messageState == iMonMessageStates.connectedRestarted)
                        this.MainLoopSM = StateMachineStates.ActivateiMonInterface;
                    else if (this.iMonDisplayInterfaceActive)
                        this.MainLoopSM = StateMachineStates.AllActive;
                    break;

                case StateMachineStates.AllActive:
                   Dictionary<string,object> ht = this.dvbvComManager.getInformationFromDVBViewer();
                    this.ShowDvbvValues(ht);
                    this.ShowDvbvValuesLcdVfd(ht);
                    if (!this.dvbvComInterfaceActive)
                    {
                        this.UnInitDisplay();
                        this.MainLoopSM = StateMachineStates.NoneActive;
                    }
                    else if (!this.iMonDisplayInterfaceActive)
                    {
                        this.MainLoopSM = StateMachineStates.ActivateiMonInterface;
                    }
                    break;
            }
            this.controlLoopActive = false;
        }

        private void ShowDvbvValues(Dictionary<string,object> ht)
        {
            if ((bool)ht["isMediaPlayback"] && !(bool)ht["isTimeshift"])
            {
                if ((bool)ht["isDVD"])
                    this.labelTitleValue.Text = ht["activeChannel"].ToString();
                else
                    this.labelTitleValue.Text = ht["mediaTitle"].ToString();
                this.labelActiveChannel.Text = string.Empty;
                this.labelPercentageValue.Text = ht["percentage"].ToString() + " %";
            }
            else
            {
                this.labelActiveChannel.Text = ht["activeChannel"].ToString();
                this.labelTitleValue.Text = ht["title"].ToString();
                this.labelPercentageValue.Text = ht["progress"].ToString() + " %";
            }
            this.labelVolumeValue.Text = (Convert.ToInt32(((double)ht["volume"] * 100))).ToString() + " %";
//            this.labelPercentageValue.Text = ht["progress"].ToString() + " %";
        }

        private void InitDisplay()
        {
            this.UnInitDisplay();
//            logger.Write("Main Control Loop: Init Display.", MessageLevel.Information);
            iMonNativeApi.iMonDisplayResult result =  iMonNativeApi.IMON_Display_Init(this.Handle, WM_DSP_PLUGIN_NOTIFY);
        }

        private void UnInitDisplay()
        {
            this.displayType = iMonNativeApi.iMonDisplayType.None;
//            logger.Write("UnInitDisplay : " + "DisplayType = " + this.displayType.ToString(), MessageLevel.Information);
            iMonNativeApi.IMON_Display_Uninit();
        }

        #endregion

        #region overrides WndProc for iMon events

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DSP_PLUGIN_NOTIFY:
                    this.OnImonMessage(m.WParam, m.LParam);
                    break;
            }
            base.WndProc(ref m);
        }

        private void OnImonMessage(IntPtr wParam, IntPtr lParam)
        {
            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();
            switch ((iMonNativeApi.iMonDisplayNotifyCode)wParam)
            {
                case iMonNativeApi.iMonDisplayNotifyCode.PluginSuccess:
                    this.displayType = (iMonNativeApi.iMonDisplayType)lParam;
//                    logger.Write("OnMessage Success : " + this.displayType.ToString() + " " + ((iMonNativeApi.iMonDisplayNotifyCode)wParam).ToString(), MessageLevel.Information, new string[] { lParam.ToString()});
                    this.labelDisplayType.Text = Enum.GetName(typeof(iMonNativeApi.iMonDisplayType), this.displayType);
                    this.messageState = iMonMessageStates.success;
                    this.showPluginMessage(wParam, false);
                    break;

                case iMonNativeApi.iMonDisplayNotifyCode.HardwareConnected:
                case iMonNativeApi.iMonDisplayNotifyCode.iMonRestarted:
//                    logger.Write("OnMessage Hardware connected/iMon restarted : " + " " + ((iMonNativeApi.iMonDisplayNotifyCode)wParam).ToString(), MessageLevel.Information, new string[] { lParam.ToString() });
                    this.messageState = iMonMessageStates.connectedRestarted;
                    this.showPluginMessage(wParam, false);
                    break;

                case iMonNativeApi.iMonDisplayNotifyCode.PluginFailed:
                case iMonNativeApi.iMonDisplayNotifyCode.HardwareDisconnected:
                case iMonNativeApi.iMonDisplayNotifyCode.iMonClosed:
                    this.displayType = iMonNativeApi.iMonDisplayType.None;
//                    logger.Write("OnMessage Error : " + "DisplayType = " + this.displayType.ToString(), MessageLevel.Information);
                    this.messageState = iMonMessageStates.failed;
                    this.labelDisplayType.Text = Enum.GetName(typeof(iMonNativeApi.iMonDisplayType),this.displayType);
                    this.showPluginMessage(lParam, true);
                    break;

                case iMonNativeApi.iMonDisplayNotifyCode.LCDTextScrollDone:
//                    logger.Write("OnImonMessage : LCDTextScrollDone", MessageLevel.Information);
                    this.OnLCDScrollFinished(EventArgs.Empty);
                    break;
            }
        }

        /// <summary>
        /// Shows the plugin message.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="error">if set to <c>true</c> [error].</param>
        private void showPluginMessage(IntPtr code,  bool error)
        {
            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();
            if (error)
            {
                if ((iMonNativeApi.iMonDisplayInitResult)code != lastErrorShown)
                {
                    switch ((iMonNativeApi.iMonDisplayInitResult) code)
                    {
                        case iMonNativeApi.iMonDisplayInitResult.PluginModeAlreadyInUse:
                            logger.Write("Display Plug-in is Already Used by Other Application.", MessageLevel.Error);
                            break;
                        case iMonNativeApi.iMonDisplayInitResult.HardwareNotConnected:
                            logger.Write("iMON HW is Not Connected.", MessageLevel.Error);
                            break;
                        case iMonNativeApi.iMonDisplayInitResult.HardwareNotSupported:
                            logger.Write("The Connected iMON HW doesn't Support Display Plug-in.", MessageLevel.Error);
                            break;
                        case iMonNativeApi.iMonDisplayInitResult.PluginModeDisabled:
                            logger.Write("Display Plug-in Mode Option is Disabled.", MessageLevel.Error);
                            break;
                        case iMonNativeApi.iMonDisplayInitResult.iMonNotResponding:
                            logger.Write("The Latest iMON is Not Installed or iMON Not Running.", MessageLevel.Error,
                                         new[]
                                             {
                                                 "iMON Display API Version 1.00.0929 or later version is needed.",
                                                 "iMON Display API uses Display Plug-in mode feature of iMON, newly added in iMON 7.91.0929 Beta version.",
                                                 "In order to make this API work properly, iMON 7.91.0929 Beta version or the later version should be installed.",
                                                 "(http://www.soundgraph.com/forums/showthread.php?t=7374).",
                                                 "Because this API must communicate with iMON, iMON should be always running.",
                                                 "Currently it supports the VFD products and LCD products."
                                             });
                            break;
                        case iMonNativeApi.iMonDisplayInitResult.Unknown:
                            logger.Write("Unknown Failure.", MessageLevel.Error);
                            break;
                    }
                    lastErrorShown = (iMonNativeApi.iMonDisplayInitResult) code;
                }
            }
            else
            {
                switch ((iMonNativeApi.iMonDisplayNotifyCode)code)
                {
                    case iMonNativeApi.iMonDisplayNotifyCode.PluginSuccess:
                        logger.Write("Plug-in Mode Inited Successfully.",MessageLevel.Information);
                        break;
                    case iMonNativeApi.iMonDisplayNotifyCode.iMonRestarted:
                        logger.Write("iMON Started and Plug-in Mode Inited.", MessageLevel.Information);
                        break;
                    case iMonNativeApi.iMonDisplayNotifyCode.HardwareConnected:
                        logger.Write("iMON HW Connected and Plug-in Mode Inited.", MessageLevel.Information);
                        break;
                }
                lastErrorShown = iMonNativeApi.iMonDisplayInitResult.Unknown;
            }          
        }

        #endregion
 
        #region buttons and toolbar events

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutDialog ad = new AboutDialog();
            ad.ShowDialog();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
//            Application.Exit();
        }    

        private void settingsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            SettingsDialog sd = new SettingsDialog();
            sd.ShowDialog();
        }


        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void toolStripMenuItemRestore_Click(object sender, EventArgs e)
        {
            this.Show();
            WindowState = FormWindowState.Normal;
        }

        private void toolStripMenuItemCloseApp_Click(object sender, EventArgs e)
        {
            this.Close();
//            Application.Exit();
        }

        #endregion

        #region connection timers

        private void timerCheckDvbvConnection_Tick(object sender, EventArgs e)
        {
            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();
            bool oldState = this.dvbvComInterfaceActive;
            this.dvbvComInterfaceActive = this.dvbvComManager.IsConnected;
            if (!oldState && this.dvbvComInterfaceActive)
                logger.Write("DVB Viewer: Successfully connected to DVBViewer COM-Interface", MessageLevel.Information);
            else if (oldState && !this.dvbvComInterfaceActive)
                logger.Write("DVB Viewer: DVBViewer COM-Interface disconnected", MessageLevel.Information);
        }

        private void timerCheckiMonConnection_Tick(object sender, EventArgs e)
        {
            var logger = EnterpriseLibraryContainer.Current.GetInstance<Logger<LogEntry>>();
            bool oldState = this.iMonDisplayInterfaceActive;
            iMonNativeApi.iMonDisplayResult result =  iMonNativeApi.IMON_Display_IsPluginModeEnabled();
            if (result == iMonNativeApi.iMonDisplayResult.ApiInPluginMode)
                this.iMonDisplayInterfaceActive = true;
            else
                this.iMonDisplayInterfaceActive = false;

            if (!oldState && this.iMonDisplayInterfaceActive)
                logger.Write("Display Plug-in Mode Option is Enabled.", MessageLevel.Information);
            else if (oldState && !this.iMonDisplayInterfaceActive)
                logger.Write("Display Plug-in Mode Option is Disabled.", MessageLevel.Information);
        }

        #endregion

        #region LCD scroll finished 

        protected virtual void OnLCDScrollFinished( EventArgs e)
        {
            LCDScrollFinishedEventHandler handler = LCDScrollFinished;
            if (handler != null)
                handler(this, e);
        }

        #endregion

        #region timer for displaying media duration and remaining time 

        private void timerDurRem_Tick(object sender, EventArgs e)
        {
            this.durationRemainingFlag = !this.durationRemainingFlag;
        }
        #endregion
    }
}
